package com.daffodil.monitor.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.cloud.nacos.NacosServiceInstance;
import com.daffodil.core.entity.JsonResult;
import com.daffodil.core.entity.TableResult;
import com.daffodil.framework.annotation.AuthPermission;
import com.daffodil.monitor.constant.MonitorConstant;
import com.daffodil.monitor.controller.model.AppInfo;
import com.daffodil.monitor.controller.model.CpuSpace;
import com.daffodil.monitor.controller.model.DiskSpace;
import com.daffodil.monitor.controller.model.JvmInfo;
import com.daffodil.monitor.controller.model.MemorySpace;
import com.daffodil.monitor.controller.model.ThreadInfo;
import com.daffodil.monitor.service.INacosService;
import com.daffodil.monitor.util.HealthUtils;
import com.daffodil.monitor.util.MetricsData;
import com.daffodil.monitor.util.MetricsData.Measurement;
import com.daffodil.util.StringUtils;
import com.daffodil.monitor.util.MetricsName;
import com.daffodil.monitor.util.MetricsUtils;

import cn.hutool.core.date.DateUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import reactor.core.publisher.Mono;
import springfox.documentation.annotations.ApiIgnore;

/**
 * -Naocs服务监控管理<br>
 * -提供一个简单的Nacos服务实例健康监控管理实现参考。
 * @author yweijian
 * @date 2022年9月30日
 * @version 2.0.0
 * @description
 */
@Api(value = "服务监控管理", tags = "服务监控管理")
@RestController
@RequestMapping(MonitorConstant.API_CONTENT_PATH)
public class SysNacosServiceController {
    
    @Autowired
    private INacosService nacosService;
    
    @ApiOperation("获取Nacos服务实例列表")
    @ApiImplicitParam(name = HttpHeaders.AUTHORIZATION, value = "登录授权令牌 Bearer token", paramType = "header", required = true)
    @AuthPermission("monitor:service:list")
    @GetMapping("/service/list")
    public Mono<TableResult> list(@ApiIgnore ServerHttpRequest request){
         List<NacosServiceInstance> services = nacosService.selectNacosServiceList();
         return Mono.just(TableResult.success(services));
    }
    
    @ApiOperation("获取实例服务器基本信息")
    @ApiImplicitParam(name = HttpHeaders.AUTHORIZATION, value = "登录授权令牌 Bearer token", paramType = "header", required = true)
    @AuthPermission("monitor:service:appinfo")
    @GetMapping("/service/appinfo")
    public Mono<JsonResult> appinfo(@ApiParam(value = "服务哈希值=MD5(服务ID + 主机地址 + 端口号)") String hashKey, @ApiIgnore ServerHttpRequest request){
        NacosServiceInstance instance = nacosService.selectNacosService(hashKey);
        String status = HealthUtils.getHealth(instance);
        Float readyTime = MetricsUtils.getMetricsDataOfNumber(instance, MetricsName.APPLICATION_READY_TIME).floatValue();
        Float startedTime = MetricsUtils.getMetricsDataOfNumber(instance, MetricsName.APPLICATION_STARTED_TIME).floatValue();
        Long startTime = MetricsUtils.getMetricsDataOfNumber(instance, MetricsName.PROCESS_START_TIME).longValue();
        Float uptime = MetricsUtils.getMetricsDataOfNumber(instance, MetricsName.PROCESS_UPTIME).floatValue();
        Integer cpuCount = MetricsUtils.getMetricsDataOfNumber(instance, MetricsName.SYSTEM_CPU_COUNT).intValue();
        AppInfo info = AppInfo.builder().status(status).readyTime(readyTime).startedTime(startedTime)
                .startTime(DateUtil.date(startTime * 1000).toJdkDate()).uptime(uptime).cpuCount(cpuCount).build();
        return Mono.just(JsonResult.success(info));
    }
    
    
    @ApiOperation("获取实例服务JVM基本信息")
    @ApiImplicitParam(name = HttpHeaders.AUTHORIZATION, value = "登录授权令牌 Bearer token", paramType = "header", required = true)
    @AuthPermission("monitor:service:jvminfo")
    @GetMapping("/service/jvminfo")
    public Mono<JsonResult> jvminfo(@ApiParam(value = "服务哈希值=MD5(服务ID + 主机地址 + 端口号)") String hashKey, @ApiIgnore ServerHttpRequest request){
        NacosServiceInstance instance = nacosService.selectNacosService(hashKey);
        Integer bufferCount = MetricsUtils.getMetricsDataOfNumber(instance, MetricsName.JVM_BUFFER_COUNT).intValue();
        Float bufferMemoryUsed = MetricsUtils.getMetricsDataOfNumber(instance, MetricsName.JVM_BUFFER_MEMORY_USED).floatValue();
        Float bufferTotalCapacity = MetricsUtils.getMetricsDataOfNumber(instance, MetricsName.JVM_BUFFER_TOTAL_CAPACITY).floatValue();
        Float gcLiveDataSize = MetricsUtils.getMetricsDataOfNumber(instance, MetricsName.JVM_GC_LIVE_DATA_SIZE).floatValue();
        Float gcMaxDataSize = MetricsUtils.getMetricsDataOfNumber(instance, MetricsName.JVM_GC_MAX_DATA_SIZE).floatValue();
        Float gcMemoryAllocated = MetricsUtils.getMetricsDataOfNumber(instance, MetricsName.JVM_GC_MEMORY_ALLOCATED).floatValue();
        Float gcMemoryPromoted = MetricsUtils.getMetricsDataOfNumber(instance, MetricsName.JVM_GC_MEMORY_PROMOTED).floatValue();
        MetricsData metricsData = MetricsUtils.getMetricsData(instance, MetricsName.JVM_GC_PAUSE);
        Integer gcPauseCount = 0;
        Float gcPauseTotalTime = 0.00F;
        if(StringUtils.isNotNull(metricsData) && StringUtils.isNotEmpty(metricsData.getMeasurements())) {
            List<Measurement> measurements = metricsData.getMeasurements();
            for (int i = 0; i < measurements.size(); i++) {
                Measurement measurement = measurements.get(i);
                if("COUNT".equals(measurement.getStatistic())) {
                    gcPauseCount = measurement.getValue().intValue();
                }
                if("TOTAL_TIME".equals(measurement.getStatistic())) {
                    gcPauseTotalTime = measurement.getValue().floatValue();
                }
            }
        }
        JvmInfo info = JvmInfo.builder().bufferCount(bufferCount)
                .bufferMemoryUsed(bufferMemoryUsed / 1024 / 1024)
                .bufferTotalCapacity(bufferTotalCapacity / 1024 / 1024)
                .gcLiveDataSize(gcLiveDataSize / 1024 / 1024)
                .gcMaxDataSize(gcMaxDataSize / 1024 / 1024)
                .gcMemoryAllocated(gcMemoryAllocated / 1024 / 1024)
                .gcMemoryPromoted(gcMemoryPromoted / 1024 / 1024)
                .gcPauseCount(gcPauseCount)
                .gcPauseTotalTime(gcPauseTotalTime)
                .build();
        return Mono.just(JsonResult.success(info));
    }
    
    @ApiOperation("获取实例服务器线程信息")
    @ApiImplicitParam(name = HttpHeaders.AUTHORIZATION, value = "登录授权令牌 Bearer token", paramType = "header", required = true)
    @AuthPermission("monitor:service:thread")
    @GetMapping("/service/thread")
    public Mono<JsonResult> thread(@ApiParam(value = "服务哈希值=MD5(服务ID + 主机地址 + 端口号)") String hashKey, @ApiIgnore ServerHttpRequest request){
        NacosServiceInstance instance = nacosService.selectNacosService(hashKey);
        Integer daemon = MetricsUtils.getMetricsDataOfNumber(instance, MetricsName.JVM_THREADS_DAEMON).intValue();
        Integer live = MetricsUtils.getMetricsDataOfNumber(instance, MetricsName.JVM_THREADS_LIVE).intValue();
        Integer states = MetricsUtils.getMetricsDataOfNumber(instance, MetricsName.JVM_THREADS_STATES).intValue();
        ThreadInfo info = ThreadInfo.builder().daemon(daemon).live(live).states(states).build();
        return Mono.just(JsonResult.success(info));
    }
    
    @ApiOperation("获取实例服务器磁盘空间使用情况")
    @ApiImplicitParam(name = HttpHeaders.AUTHORIZATION, value = "登录授权令牌 Bearer token", paramType = "header", required = true)
    @AuthPermission("monitor:service:disk")
    @GetMapping("/service/disk")
    public Mono<JsonResult> disk(@ApiParam(value = "服务哈希值=MD5(服务ID + 主机地址 + 端口号)") String hashKey, @ApiIgnore ServerHttpRequest request){
        DiskSpace space = new DiskSpace();
        NacosServiceInstance instance = nacosService.selectNacosService(hashKey);
        Float free = MetricsUtils.getMetricsDataOfNumber(instance, MetricsName.DISK_FREE).floatValue();
        Float total = MetricsUtils.getMetricsDataOfNumber(instance, MetricsName.DISK_TOTAL).floatValue();
        space.setFree(free / 1024 / 1024 / 1024); //Byte / KB / MB /GB
        space.setTotal(total / 1024 / 1024 / 1024);
        return Mono.just(JsonResult.success(space));
    }
    
    @ApiOperation("获取实例服务器CPU使用情况")
    @ApiImplicitParam(name = HttpHeaders.AUTHORIZATION, value = "登录授权令牌 Bearer token", paramType = "header", required = true)
    @AuthPermission("monitor:service:cpu")
    @GetMapping("/service/cpu")
    public Mono<JsonResult> cpu(@ApiParam(value = "服务哈希值=MD5(服务ID + 主机地址 + 端口号)") String hashKey, @ApiIgnore ServerHttpRequest request){
        CpuSpace space = new CpuSpace();
        NacosServiceInstance instance = nacosService.selectNacosService(hashKey);
        Float process = MetricsUtils.getMetricsDataOfNumber(instance, MetricsName.PROCESS_CPU_USAGE).floatValue();
        Float system = MetricsUtils.getMetricsDataOfNumber(instance, MetricsName.SYSTEM_CPU_USAGE).floatValue();
        space.setProcess(process);
        space.setSystem(system);
        return Mono.just(JsonResult.success(space));
    }
    
    @ApiOperation("获取实例服务器内存空间使用情况")
    @ApiImplicitParam(name = HttpHeaders.AUTHORIZATION, value = "登录授权令牌 Bearer token", paramType = "header", required = true)
    @AuthPermission("monitor:service:memory")
    @GetMapping("/service/memory")
    public Mono<JsonResult> memory(@ApiParam(value = "服务哈希值=MD5(服务ID + 主机地址 + 端口号)") String hashKey, @ApiIgnore ServerHttpRequest request){
        MemorySpace space = new MemorySpace();
        NacosServiceInstance instance = nacosService.selectNacosService(hashKey);
        Float max = MetricsUtils.getMetricsDataOfNumber(instance, MetricsName.JVM_MEMORY_MAX).floatValue();
        Float used = MetricsUtils.getMetricsDataOfNumber(instance, MetricsName.JVM_MEMORY_USED).floatValue();
        space.setMax(max / 1024 / 1024); //Byte / KB / MB
        space.setUsed(used / 1024 / 1024);
        space.setUsage(max > 0 ? used / max : 0.00F);
        return Mono.just(JsonResult.success(space));
    }
    
}
